/**
 * Copyright 2012-2020 The MicroRpc Authors
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.ww.microrpc.rpc;

import com.ww.microrpc.client.Client;
import com.ww.microrpc.netty.handler.client.SendCallback;
import com.ww.microrpc.netty.RequestHolder;
import com.ww.microrpc.netty.protocol.ResponseMessage;
import com.ww.microrpc.netty.common.ConvertUtil;
import com.ww.microrpc.netty.common.SequenceIdGenerator;

import java.util.Arrays;

final class SynchronousMethodHandler implements InvocationHandlerFactory.MethodHandler {

    private final MethodMetadata metadata;
    private final Target<?> target;
    private final Client client;

    private SynchronousMethodHandler(Target<?> target,
                                     Client client,
                                     MethodMetadata metadata) {
        this.target = target;
        this.client = client;
        this.metadata = metadata;
    }

    @Override
    public Object invoke(Object[] argv) throws Throwable {
        RequestHolder request = this.buildRequest(argv);
        ResponseMessage response = client.execute(request);
        Object result = ConvertUtil.convertObject(response, request.getReturnType());
        return result;
    }

    private RequestHolder buildRequest(Object[] argv) {
        RequestHolder request = new RequestHolder();
        request.setUrl(target.url());
        request.setClientName(target.name());
        request.setRequestId(SequenceIdGenerator.nextId());
        request.setMethodName(metadata.methodName());
        request.setClassName(metadata.className());
        request.setSerializer(target.serializer());
        request.setReturnType(metadata.method().getReturnType());
        Class<?>[] parameterTypes = metadata.method().getParameterTypes();
        Object o = argv[argv.length - 1];
        if (o instanceof SendCallback) {
            Object[] argvNew = Arrays.copyOfRange(argv, 0, argv.length - 1);
            Class<?>[] typeNew = Arrays.copyOfRange(parameterTypes, 0, argv.length - 1);
            request.setCallback((SendCallback) o);
            request.setParameterTypes(typeNew);
            request.setParameters(argvNew);
        } else {
            request.setParameterTypes(parameterTypes);
            request.setParameters(argv);
        }
        return request;
    }

    static class Factory {

        private final Client client;

        Factory(Client client) {
            this.client = client;
        }

        public InvocationHandlerFactory.MethodHandler create(
                Target<?> target,
                MethodMetadata md
        ) {
            return new SynchronousMethodHandler(target, client, md);
        }
    }
}
